#include "DataCore.h"
#include "Engine.h"
#include "TGAUtil.h"

DataCore* DataCore::dataCore;

const GLuint CHANNEL_MAP[] = {0, 1, 2, GL_RGB, GL_RGBA};
extern char workingFolderName[50];

//convert char ABCDF to number
int DataCore::SwitchCharToNumver ( char ch )
{
	switch (ch)
	{
	case 'A':
	case 'a':
		return 10;
	case 'B':
	case 'b':
		return 11;
	case 'C':
	case 'c':
		return 12;
	case 'D':
	case 'd':
		return 13;
	case 'E':
	case 'e':
		return 14;
	case 'F':
	case 'f':
		return 15;
	default:
		return (ch - '0');
	}
}

//Ox1006 -> 6 not use 0x1 only conver 006
int DataCore::ConvertHexToDec (char* hec )
{
	int a2, a1, a0;
	a2 = SwitchCharToNumver ( hec[3] );
	a1 = SwitchCharToNumver ( hec[4] );
	a0 = SwitchCharToNumver ( hec[5] );
	
	return ( a2*16*16 + a1*16 + a0 );
}

DataCore::DataCore()
{
	dataCore = NULL;
}

DataCore::~DataCore()
{	
}

DataCore* DataCore::GetData()
{
	if ( dataCore == NULL )
		dataCore = new DataCore();
	return dataCore;
}

void DataCore::Decode(FILE *fp, ImageData &image)
{           
    png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop infoPtr = png_create_info_struct(pngPtr);

    png_init_io(pngPtr, fp);
    png_read_info(pngPtr, infoPtr);
		
    image.width = png_get_image_width(pngPtr, infoPtr);
    image.height = png_get_image_height(pngPtr, infoPtr);

    //bits per CHANNEL! note: not per pixel!
    png_uint_32 bitdepth   = png_get_bit_depth(pngPtr, infoPtr);

    //Number of channels
    image.nChannels   = png_get_channels(pngPtr, infoPtr);

    //Color type. (RGB, RGBA, Luminance, luminance alpha... palette... etc)
    png_uint_32 color_type = png_get_color_type(pngPtr, infoPtr);

    switch (color_type) {
        case PNG_COLOR_TYPE_PALETTE:
            png_set_palette_to_rgb(pngPtr);        
            image.nChannels = 3;           
            break;
    }

    /*if the image has a transperancy set.. convert it to a full Alpha channel..*/
    if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
        png_set_tRNS_to_alpha(pngPtr);
        image.nChannels += 1;
    }

    //We don't support 16 bit precision.. so if the image Has 16 bits per channel
        //precision... round it down to 8.
    if (bitdepth == 16)
        png_set_strip_16(pngPtr);

    image.data = new char[image.width * image.height * bitdepth * image.nChannels >> 3];

    const unsigned int stride = image.width * bitdepth * image.nChannels >> 3;
    png_bytep* rowPtrs = new png_bytep[image.height];

    for (size_t i = 0; i < image.height; i++) {
        int q = (i) * stride;
        rowPtrs[i] = (png_bytep) image.data + q;
    }   

    png_read_image(pngPtr, rowPtrs);

    delete [] rowPtrs;

    png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
       
}

GLuint DataCore::LoadTexture2D(char *pngFileName)
{	
	LOGE("\nPNG file name: %s", pngFileName);
	ImageData textureData;
	GLuint textureID = 0;

	FileSystem* fileSystem = FileSystem::GetFileSystem();
	FILE* fp = fileSystem->Open(pngFileName, "rb"); //rb : read binary	
    if (!fp) 
    {
        LOGE("\nCan not load texture: %s", pngFileName);
        return 0;
    }

    Decode(fp, textureData);
	glGenTextures(1, &textureID);	
    glBindTexture(GL_TEXTURE_2D, textureID);	
	if (textureData.nChannels == 4)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureData.width, textureData.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData.data);
	}
	else
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureData.width, textureData.height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData.data);
	}
        
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

	glGenerateMipmap(GL_TEXTURE_2D);	
	return textureID;
}
//Load one Textures to map on model
GLuint DataCore::LoadTexture(char *filename)
{
	int width,heigth;
	char realPath[100];

	strcpy(realPath, workingFolderName);
	strcat(realPath, filename);

	GLuint textureID;     //GLint textureID[num]; if gen textures

	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_2D,textureID);

	LOGE("\ntga file: %s", realPath);
	char *imageData = esLoadTGA(realPath ,&width, &heigth);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	delete imageData;
	return textureID;
}

//Load model
Model* DataCore::LoadModel(const char *filename)
{
	Model *model = new Model();
	vector<Position> position;
	vector<Texcoord> texcoord;
	vector<Index> indices;
	char line[100];
	char realPath[100];

	int i;
	FileSystem* fileSystem = FileSystem::GetFileSystem();
	
	strcpy(realPath, workingFolderName);
	strcat(realPath, filename);

	FILE *file = fileSystem->Open(realPath, "r");

	if(file)
	{
		char ch;
		while(!feof(file))
		{
			ch = fgetc(file);
			if(ch == 'v')
			{
				ch = fgetc(file);
				if(ch == ' ')
				{
					Position p;
					fscanf(file, "%f %f %f", &p.x, &p.y, &p.z);
					position.push_back(p);
				}
				else
					if(ch == 't')
					{
						Texcoord t;						
						//fscanf(file, "%f %f", &t.u, &t.v);
						fgets(line,sizeof(line),file);
						sscanf(&line[1],"%f%f",&t.u,&t.v);
						texcoord.push_back(t);
					}
			}
			else
			{
				if(ch == 'f')
				{					
					for(int i = 0; i < 3; i++)
					{	
						Index id;
						fscanf(file, "%d/%d/%d", &id.iv, &id.ivt, &id.ivn);
						indices.push_back(id);
					}
				}
			}

			if(ch != '\n' && ch != 't')
			{
				char buff[256];
				fgets(buff, 256, file);
			}		

		}

		model->numvertices = indices.size();
		model->position = new Position[model->numvertices];
		model->texcoord = new Texcoord[model->numvertices];

		for(i = 0; i < model->numvertices; i++)
		{
			model->position[i] = position[indices[i].iv -1 ];
			model->texcoord[i] = texcoord[indices[i].ivt - 1];
		}
		
		fclose(file);
	}
	else
	{
		LOGE("Can not read file %s", realPath);
	}
	return model;
}

//Load one Texture Cube 3D
GLuint DataCore::LoadTexture3D(char *fPOSITIVE_X, char *fNEGATIVE_X, char *fPOSITIVE_Y, char *fNEGATIVE_Y, char *fPOSITIVE_Z, char *fNEGATIVE_Z)
{
	int width, heigth;
	char realPath[100];

	GLuint texture3D;     
	glGenTextures(1, &texture3D);

	glBindTexture(GL_TEXTURE_CUBE_MAP, texture3D);

	char *imageData[6];
	strcpy(realPath, workingFolderName);
	strcat(realPath, fPOSITIVE_X);
	imageData[0] = esLoadTGA(fPOSITIVE_X ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[0]);

	strcpy(realPath, workingFolderName);
	strcat(realPath, fNEGATIVE_X);
	imageData[1] = esLoadTGA(fNEGATIVE_X ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[1]);

	strcpy(realPath, workingFolderName);
	strcat(realPath, fPOSITIVE_Y);
	imageData[2] = esLoadTGA(fPOSITIVE_Y ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[2]);

	strcpy(realPath, workingFolderName);
	strcat(realPath, fNEGATIVE_Y);
	imageData[3] = esLoadTGA(fNEGATIVE_Y ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[3]);

	strcpy(realPath, workingFolderName);
	strcat(realPath, fPOSITIVE_Z);
	imageData[4] = esLoadTGA(fPOSITIVE_Z ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[4]);

	strcpy(realPath, workingFolderName);
	strcat(realPath, fNEGATIVE_Z);	
	imageData[5] = esLoadTGA(fNEGATIVE_Z ,&width, &heigth);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, width, heigth, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData[5]);
	
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	for (int i = 0; i < 6; i++)
	{
		delete imageData[i];
	}
	return texture3D;
};


GLint* DataCore::ReadLevel(char * filename)
{
	//FileSystem* fileSystem = FileSystem::GetFileSystem();
	//FILE* file = fileSystem->Open(filename,"r");
	GLint *Level;
	//GLint i;
	//Level = new GLint[ROW * COLUMN];
	
   /* for(int i = 0;i < ROW; i++)
	{
	    for(int j = 0; j< COLUMN ; j++)
		{
	        fscanf(file,"%d",&Level[i * COLUMN + j]);
	    }
	  }*/

	return Level;
}



Sprite* DataCore::LoadSprite(char * packName)
{
	//make correct path  
	//ex input menu.pack -> working/menu.pack
	char line[200], temp[100];
	int maxValueModule, maxValueFrame, maxValueAnim, count, countRect;
	int ModuleID = 0;
	int frameID = 0;
	int animID  = 0;
	bool isLoadFrame = 0;
	bool isLoadAnim = 0;

	//real path
	char realPath[100];
	strcpy(realPath, workingFolderName);
	strcat(realPath, packName);
		
	LOGE("\nSprite file name: %s", realPath);	
	Sprite *sprite = new Sprite();
		
	FileSystem* fileSystem = FileSystem::GetFileSystem();
	FILE *file = fileSystem->Open(realPath, "r");
	if( file == NULL)
	{
		LOGE("\nCan not read file: %s", realPath);
		return NULL;
	}

	//read max value module, frame and anim
	while(!feof(file))
	{			
		fgets(line,sizeof(line),file);	

		//max value of modules			
		if (line[0] == 'M' && line[1] == 'A' && line[2] =='X' && line[3] =='_' && line[4] =='M' && line[5] =='D' )
		{
			sscanf(&line[1], "%s" "%d", &temp, &maxValueModule);
		}
		//max value of frames
		else if (line[0] == 'M' && line[1] == 'A' && line[2] =='X' && line[3] =='_' && line[4] =='F' && line[5] =='M' )
		{
			sscanf(&line[1], "%s" "%d", &temp, &maxValueFrame);
		}
		//max value of anims
		else if (line[0] == 'M' && line[1] == 'A' && line[2] =='X' && line[3] =='_' && line[4] =='A' && line[5] =='N' )
		{
			sscanf(&line[1], "%s" "%d", &temp, &maxValueAnim);
		}		
	}
	fclose(file);

	sprite->modules = new Module[maxValueModule];
	sprite->frames = new Frame[maxValueFrame];
	sprite->anims = new Anim[maxValueAnim];
	
	file = fileSystem->Open(realPath, "r");
	while(!feof(file))
	{			
		fgets(line,sizeof(line),file);	

		//image and size			
		if (line[0] == 'I' && line[1] == 'M' && line[2] =='A' && line[3] =='G' && line[4] =='E')
		{
			char imageName[30];
			sscanf(&line[1], "%s" "%s" "%s" "%d" "%d", &temp, &imageName, &temp, &sprite->width, &sprite->height );
			//real path of image
			char* realImagePath = new char[ strlen(imageName) + strlen( workingFolderName ) ];
			strcpy (realImagePath, workingFolderName );
			strcat (realImagePath, imageName );
			sprite->imageID = LoadTexture2D ( realImagePath );
		}
		//modules
		else if (line[0] == 'M' && line[1] == 'D' )
		{
			if ( VGPConfig::sprite_useModule || VGPConfig::sprite_useFrame || VGPConfig::sprite_useAnim )
			{
				int x, y, w, h;
				char ID[10];
				sscanf(&line[1], "%s" "%s" "%d" "%d" "%d" "%d", &temp, &ID, &x, &y, &w, &h );
				ModuleID = ConvertHexToDec(ID);
				sprite->modules[ModuleID].x = x;
				sprite->modules[ModuleID].y = y;
				sprite->modules[ModuleID].w = w;
				sprite->modules[ModuleID].h = h;
			}

		}
		//frames		
		else if ( line[0] == 'F' && line[1] == 'R'&& line[2] == 'A' && line[3] == 'M' && line[4] == 'E' || isLoadFrame )
		{
			if ( VGPConfig::sprite_useFrame || VGPConfig::sprite_useAnim )
			{
				if ( !isLoadFrame )
				{
					int numOfFrame;
					sscanf(&line[1], "%s" "%s" "%d", &temp, &temp, &numOfFrame );
					fgets(line,sizeof(line),file); //skip //{
					memset( line, 0, sizeof(line) );
					fgets(line,sizeof(line),file); //read id		
					frameID =  ConvertHexToDec(line);
					sprite->frames[frameID].fmodule = new FModule[numOfFrame + 1]; //+ 1 with value -1 (< 0)
					
					//new max rect is 10
					if ( VGPConfig::sprite_useFrameRect )
						sprite->frames[frameID].rect = new Module[VGPConfig::sprite_FrameRect_Max + 1]; //+ 1 with value x=y=w=h=-1 (< 0)
					else
						sprite->frames[frameID].rect = new Module[1 + 1];
					count = 0;
					countRect = 0;
					isLoadFrame = 1;
				}
				else
				{
					if ( line[0] == 'F' && line[1] == 'M' )
					{
						int x, y, flag = 0;
						char flip_X[10] = {'\0'}, flip_Y[10] = {'\0'}, Rot_90[10]= {'\0'}, ID[10];
						sscanf(&line[1], "%s" "%s" "%d" "%d" "%s" "%s" "%s" "%s" , &temp, &ID, &x, &y, &flip_X, &flip_Y, &Rot_90 );

						//flag:					
						if ( flip_X[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( flip_X[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( flip_X[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;
					
						if ( flip_Y[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( flip_Y[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( flip_Y[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;
					
						if ( Rot_90[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( Rot_90[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( Rot_90[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;

						sprite->frames[frameID].fmodule[count].FM_ID = ConvertHexToDec(ID);
						sprite->frames[frameID].fmodule[count].x = x;
						sprite->frames[frameID].fmodule[count].y = y;
						sprite->frames[frameID].fmodule[count].flag = flag;
						//LOGE ("\n %d  %d ", sprite->frames[frameID].fmodule[count].x, sprite->frames[frameID].fmodule[count].y );
						count++;
					}
					else if ( line[0] == 'R' && line[1] == 'C' && countRect < VGPConfig::sprite_FrameRect_Max && VGPConfig::sprite_useFrameRect ) //load rect
					{
						int x1, x2, y1, y2;
						sscanf(&line[1], "%s" "%d" "%d" "%d" "%d", &temp, &x1, &y1, &x2, &y2 );					
						sprite->frames[frameID].rect[countRect].x = x1;
						sprite->frames[frameID].rect[countRect].y = y1;
						sprite->frames[frameID].rect[countRect].w = x2 - x1;
						sprite->frames[frameID].rect[countRect].h = y2 - y1;
						countRect++;

					}
					else if ( line[0] == '}' )
					{
						isLoadFrame = 0;
						sprite->frames[frameID].fmodule[count].FM_ID = -1;	
						sprite->frames[frameID].rect[countRect].x = sprite->frames[frameID].rect[countRect].y = sprite->frames[frameID].rect[countRect].w = sprite->frames[frameID].rect[countRect].h = -1;
					}
				}
			}
		}
		//anims
		else if ( line[0] == 'A' && line[1] == 'N'&& line[2] == 'I' && line[3] == 'M' || isLoadAnim )
		{
			if ( VGPConfig::sprite_useAnim )
			{
				if ( !isLoadAnim )
				{
					int numOfAnim;
					sscanf(&line[1], "%s" "%s" "%d", &temp, &temp, &numOfAnim );
					fgets(line,sizeof(line),file); //skip //{
					memset( line, 0, sizeof(line) );
					fgets(line,sizeof(line),file); //read id	
					animID = ConvertHexToDec(line);					
					sprite->anims[animID].aframe = new AFrame[numOfAnim + 1]; //+ 1 with value -1 (< 0)
					count = 0;
					isLoadAnim = 1;
				}
				else
				{
					if ( line[0] == 'A' && line[1] == 'F' )
					{
						int time,x, y, flag = 0;
						char flip_X[10], flip_Y[10],Rot_90[10], ID[10];
						sscanf(&line[1], "%s" "%s" "%d" "%d" "%d" "%s" "%s" "%s" "%s" , &temp, &ID, &time, &x, &y, &flip_X, &flip_Y, &Rot_90 );
						//flag:
						if ( flip_X[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( flip_X[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( flip_X[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;
					
						if ( flip_Y[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( flip_Y[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( flip_Y[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;
					
						if ( Rot_90[6] == 'X' ) //flip_x
							flag = flag | VGPLib::FLIP_X;
						else if ( Rot_90[6] == 'Y' ) //flip_y
							flag = flag | VGPLib::FLIP_Y;					
						else if ( Rot_90[6] == '0' ) //rot_90
							flag = flag | VGPLib::ROT_90;
					
					
						sprite->anims[animID].aframe[count].AF_ID = ConvertHexToDec(ID);
						sprite->anims[animID].aframe[count].time = time;
						sprite->anims[animID].aframe[count].x = x;
						sprite->anims[animID].aframe[count].y = y;
						sprite->anims[animID].aframe[count].flag = flag;
						count++;
					}
					else if ( line[0] == '}' )
					{
						isLoadAnim = 0;
						sprite->anims[animID].aframe[count].AF_ID = -1;
					}
				}
			}
		}
				
		memset( line, 0, sizeof(line) );		
	}
		
	fclose(file);
		
	return sprite;
}

//Load gui
void DataCore::LoadGui ( char *guiPackName )
{
	char *realPath = new char [strlen ( guiPackName ) + strlen ( workingFolderName )];
	strcpy ( realPath, workingFolderName );
	strcat ( realPath, guiPackName );
	SceneEngine::GetSceneEngine()->LoadScene ( realPath );
}

